﻿using System.Collections.Generic;
using System.Diagnostics;

namespace TakePhotoTogether.Communication
{
    public class WeightedFairDataQueue : DataQueue
    {
        private Dictionary<int, Queue<DataPacket>> priorizedDataBuffer = null;
        private readonly int[] priorizedDataFlowCounter = null;
        private const int MAX_PRIORITY_LEVEL = DataPacketFactory.MAX_PRIORITY_LEVEL;
        private const int MAX_BYTE_RATE_FLOW = 500;
        private float normalizationFactor = 0.0f;

        private float totalPacketsBytes = 0.0f;
        private readonly float[] packetBytesByPriority = new float[MAX_PRIORITY_LEVEL];

        public WeightedFairDataQueue()
            : base()
        {
            priorizedDataFlowCounter = new int[MAX_PRIORITY_LEVEL];

            InitializeBuffer();
        }

        private void InitializeBuffer()
        {
            for (int b = 1; b <= MAX_PRIORITY_LEVEL; b++)
            {
                if (!priorizedDataBuffer.ContainsKey(b))
                {
                    priorizedDataBuffer.Add(b, new Queue<DataPacket>());
                }
                priorizedDataFlowCounter[b-1] = 0;
                normalizationFactor += DataPacketFactory.GetPacketBandwidth(b);
            }
        }

        public override void Clear()
        {
            foreach (KeyValuePair<int, Queue<DataPacket>> keyValuePair in priorizedDataBuffer)
            {
                keyValuePair.Value.Clear();
            }

            InitializeBuffer();
        }

        protected override void CreateQueue()
        {
            if (priorizedDataBuffer == null)
                priorizedDataBuffer = new Dictionary<int, Queue<DataPacket>>();
        }

        public override void Enqueue(DataPacket packet)
        {
            priorizedDataBuffer[DataPacketFactory.GetPriority(packet)].Enqueue(packet);
        }

        public override DataPacket Dequeue()
        {
            //Debug.WriteLine("WeightedFairDataQueue::Dequeue - 1");

            int currentPriorityLevel = getCurrentPriorityLevel();

            //Debug.WriteLine("WeightedFairDataQueue::Dequeue - 2");

            if (currentPriorityLevel == -1)
            {
                for (int b = 1; b <= MAX_PRIORITY_LEVEL; b++)
                {
                    priorizedDataFlowCounter[b-1] = priorizedDataFlowCounter[b-1] % MAX_BYTE_RATE_FLOW;
                }

                currentPriorityLevel = getCurrentPriorityLevel();
            }

            if (currentPriorityLevel == -1)
            {
                return null;
            }

            //Debug.WriteLine("WeightedFairDataQueue::Dequeue - 3");
            //Debug.WriteLine("WeightedFairDataQueue::Dequeue - currentPriorityLevel: " + currentPriorityLevel);
            //Debug.WriteLine("WeightedFairDataQueue::Dequeue - count: " + priorizedDataBuffer[currentPriorityLevel].Count);

            DataPacket packet = priorizedDataBuffer[currentPriorityLevel].Dequeue();

            if (packet == null)
            {
                return null;
            }

           // Debug.WriteLine("WeightedFairDataQueue::Dequeue - 4");

            // Updated data flow counter in order to implement WFQ
            priorizedDataFlowCounter[currentPriorityLevel - 1] += (int)( packet.DataSize * getInverseWeight(currentPriorityLevel));

            packetBytesByPriority[currentPriorityLevel - 1] += packet.DataSize;
            totalPacketsBytes += packet.DataSize;

            //Debug.WriteLine("\nCurrent priority {0} has {1}% of band width \n", currentPriorityLevel, packetBytesByPriority[currentPriorityLevel - 1]/totalPacketsBytes);

            return packet;
        }

        private float getInverseWeight(int priorityLevel)
        {
            return normalizationFactor / DataPacketFactory.GetPacketBandwidth(priorityLevel);
        }

        private int getCurrentPriorityLevel()
        {
            for (int p = 1; p <= MAX_PRIORITY_LEVEL; p++)
            {
                if (priorizedDataBuffer[p].Count > 0 && priorizedDataFlowCounter[p - 1] < MAX_BYTE_RATE_FLOW)
                {
                    return p;
                }
            }

            return -1;
        }

        public override int Count()
        {
            int numberOfPackets = 0;

            for (int b = 0; b < MAX_PRIORITY_LEVEL; b++)
            {
                numberOfPackets = numberOfPackets + priorizedDataBuffer[b+1].Count;
            }

            return numberOfPackets;
        }

        public override int GetNumberOfPacketsOnQueue(int messageType)
        {
            Debug.Assert(messageType > 0 && messageType <= MAX_PRIORITY_LEVEL, "Wrong message type");

            return priorizedDataBuffer[messageType].Count;
        }

        public override int GetNumberOfPacketsOnQueue()
        {
            return priorizedDataBuffer.Count;
        }
    }
}
